using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using System.Text;
using Microsoft.Win32;
using System.IO;
using System.Runtime.InteropServices;
using ProcessMemoryReaderLib;
using System.Threading;
using WinAmpSDK;

namespace LANamp
{
    
    public class Winamp
    {

        private IntPtr handle;
        private bool running = false;
        private bool orgRepeatStatus = true;
        public delegate void StateChangeEvent();
        public event StateChangeEvent Started;
        public event StateChangeEvent Exited;

        public string WA_Path;
        public Process WinAmpProcess;
        
        public bool Running
        {
            get
            {
                return running;
            }
        }
        public IntPtr Handle
        {
            get
            {
                return handle;
            }
        }

        #region Type-conversions
        private static int ToInt32(bool b)
        {
            return (b) == true ? 1 : 0; //Shorthand for if statement
        }
        private static bool ToBool(int i)
        {
            return (i) == 1 ? true : false;
        }
        #endregion

        public void Start()
        {
            Thread WinampThread = new Thread(new ThreadStart(startWinAmp));
            WinampThread.IsBackground = true;
            WinampThread.Name = "WinampFinder";
            WinampThread.Start();
        }

        public void CleanUp()
        {
            RepeatStatus = orgRepeatStatus;
        }

        public void Exit()
        {
            if (WinAmpProcess != null)
            {
                CleanUp();
                WinAmpProcess.CloseMainWindow();
            }
            running = false;
        }

        public void Initialize()
        {
            SendToWinamp(0, WM_USER_MSGS.WA_SAVE_PLAYLIST);
            Stop();
            ClearPlaylist();
            RepeatStatus = false;
        }

        public Winamp()
        {
        }

        private void startWinAmp()
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Winamp");
            if (key != null)
            {
                WA_Path = key.GetValue("").ToString();
            }
            IntPtr hWnd = Win32.FindWindow("Winamp v1.x", null);

            if (hWnd.ToInt32() == 0)
            {
                if (WA_Path != null)
                {
                    WinAmpProcess = Process.Start(WA_Path + "\\Winamp.exe");
                }
                else
                {
                    throw new Exception("No installation of winamp detected.");
                }
                while (hWnd.ToInt32() == 0)
                {
                    hWnd = Win32.FindWindow("Winamp v1.x", null);
                    Thread.Sleep(500);
                }
            }
            else
                WinAmpProcess = Process.GetProcessesByName("Winamp")[0];

            running = true;
            handle = hWnd;
            WinAmpProcess.EnableRaisingEvents = true;
            WinAmpProcess.Exited += new EventHandler(WinAmpProcess_Exited);
            orgRepeatStatus = RepeatStatus;
            if (Started != null) Started();
        }

        void WinAmpProcess_Exited(object sender, EventArgs e)
        {
            handle = (IntPtr)0;
            WinAmpProcess = null;
            running = false;
            if (Exited != null) Exited();
        }

        #region Winamp Send commands
        public static int SendCommandToWinamp(IntPtr hWnd, WM_COMMAND_MSGS Command, uint lParam)
        {
            return Win32.SendMessage(hWnd, Win32.WM_COMMAND, (int)Command, lParam);
        }

        public int SendToWinamp(int nData, WM_USER_MSGS msg)
        {
            return SendToWinamp(WA_MsgTypes.WM_USER, nData, (int)msg);
        }
        public int SendToWinamp(int nData, WA_IPC msg)
        {
            return SendToWinamp(WA_MsgTypes.WM_USER, nData, (int)msg);
        }

        public int SendToWinamp(int nData, WM_COMMAND_MSGS msg)
        {
            return SendToWinamp(WA_MsgTypes.WM_COMMAND, (int)msg, 0);
        }
        public int SendToWinamp(ref Win32.COPYDATASTRUCT data) //not sure about this one, i think the only message you can send for copydata is 0
        {
            return Win32.SendMessageA(this.Handle, Win32.WM_COPYDATA, 0, ref data);
        }
        public int SendToWinamp(WA_MsgTypes msgType, int nData, int nMsg)
        {
            return Win32.SendMessage(this.Handle, (int)msgType, nData, (uint)nMsg);
        }

#endregion
        #region Winamp Commands
        public void AppendToPlayList(string fileName)
        {

            Win32.COPYDATASTRUCT cds;
            //cds.dwData = (IntPtr)100;
            cds.dwData = (IntPtr)WA_IPC.IPC_ENQUEUEFILE;
            cds.lpData = fileName;
            cds.cbData = fileName.Length + 1;

            //Win32.SendMessageA(this.Handle, Win32.WM_COPYDATA, 0, ref cds);
            SendToWinamp(ref cds);
        }
        public void AppendToPlayList(string[] fileNames)
        {
            foreach (string sFile in fileNames)
            {
                AppendToPlayList(sFile);
            }
        }
        public void AppendToPlayList(FileInfo fileName)
        {
            AppendToPlayList(fileName.FullName);
        }
        public void AppendToPlayList(FileInfo[] fileNames)
        {
            foreach (FileInfo fileName in fileNames)
            {
                AppendToPlayList(fileName);
            }
        }

        public bool ShuffleStatus
        {
            get
            {
                int length = SendToWinamp(0, WM_USER_MSGS.WA_GET_SHUFFLE);
                    //Win32.SendMessage(this.Handle, Win32.WM_USER, 0, (uint)WM_USER_MSGS.WA_GET_SHUFFLE);
                bool status = ToBool(length); 
                return status; 
            }
            set
            {
                int status = ToInt32(value);
                SendToWinamp(status, WM_USER_MSGS.WA_SET_SHUFFLE);
                //Win32.SendMessage(this.Handle, Win32.WM_USER, status, (uint)WM_USER_MSGS.WA_SET_SHUFFLE);
            }
        }

        public bool ToggleShuffle()
        {
            this.ShuffleStatus = !this.ShuffleStatus;
            return this.ShuffleStatus;
            //it is possible to use a WM_COMMAND to do this, but we cannot return the new value then.
        }

        public bool RepeatStatus
        {
            get
            {
                int length = SendToWinamp(0, WM_USER_MSGS.WA_GET_REPEAT);
                    //Win32.SendMessage(this.Handle, Win32.WM_USER, 0, (uint)WM_USER_MSGS.WA_GET_REPEAT);
                return ToBool(length);
            }
            set
            {
                int status = ToInt32(value);
                SendToWinamp(status, WM_USER_MSGS.WA_SET_REPEAT);
            }
        }
        public string CurrentSong
        {
            get
            {
                int trackpos = SendToWinamp(0, WM_USER_MSGS.WA_GET_PLAYLIST_POS);
                IntPtr mpoint;
                mpoint = (IntPtr)SendToWinamp(trackpos, WM_USER_MSGS.WA_GET_TRACK_TITLE);
                if (mpoint.ToInt32() > 0)
                {
                    ProcessMemoryReader pmr = new ProcessMemoryReader();
                    pmr.ReadProcess = WinAmpProcess;
                    pmr.OpenProcess();
                    byte[] data = new byte[256];
                    int length;
                    data = pmr.ReadProcessMemory(mpoint, (uint)data.Length, out length);
                    string datstr = Encoding.ASCII.GetString(data).Split(new char[1] {'\0'})[0];
                    return datstr;
                }
                else
                {
                    return "(none)";
                }
            }
        }

        public string TrackLength
        {
            get
            {
                int seconds = SendToWinamp(1, WM_USER_MSGS.WA_GET_PLAYBACK_INFO);
                if (seconds > 0)
                {
                    TimeSpan time = new TimeSpan(0, 0, seconds);
                    return time.Minutes.ToString("d2") + ":" + time.Seconds.ToString("d2");
                }
                else
                {
                    return "00:00";
                }
            }
        }

        public string TrackPosition
        {
            get
            {
                int mseconds = SendToWinamp(0, WM_USER_MSGS.WA_GET_PLAYBACK_INFO);
                if (mseconds > 0)
                {
                    TimeSpan time = new TimeSpan(0, 0, 0, 0, mseconds);
                    return time.Minutes.ToString("d2") + ":" + time.Seconds.ToString("d2");
                }
                else
                {
                    return "00:00";
                }
            }
        }

        public string Version
        {
            get
            {
                int result = 0;
                while (result == 0) result = SendToWinamp(0, WA_IPC.IPC_GETVERSION);
                string ver = result.ToString();
                switch (result)
                {
                    case 0x1551: { ver = "1.55"; break; }
                    case 0x16a0: { ver = "1.6b"; break; }
                    case 0x16af: { ver = "1.60"; break; }
                    case 0x16b0: { ver = "1.61"; break; }
                    case 0x16b1: { ver = "1.62"; break; }
                    case 0x16b3: { ver = "1.64"; break; }
                    case 0x16b4: { ver = "1.666"; break; }
                    case 0x16b5: { ver = "1.69"; break; }
                    default:
                    {
                        string brk = result.ToString("x");
                        ver = brk.Substring(0,1) + "."+brk.Substring(2,1) + brk.Substring(3);
                        break;
                    }
                }
                return ver;
            }
        }
        public bool ToggleRepeat()
        {
            this.RepeatStatus = !this.RepeatStatus;
            return this.RepeatStatus;
            //it is possible to use a WM_COMMAND to do this, but we cannot return the new value then.
        }

        public int Volume
        {
            //volume is 0 - 255
            get
            {
                return 0;//SendToWinamp(0, WM_USER_MSGS.wa_get;
            }
            set
            {
                int vol = value;
                if (vol <= 0)
                {
                    vol = 0;
                }
                else if (vol >= 100)
                {
                    vol = 100;
                }
                int nWaVol = (int)(vol / 100d * 255);
                SendToWinamp(nWaVol, WM_USER_MSGS.WA_SET_VOL);
            }
        }

        public int PlayStatus
        {
            get
            {
                int result = SendToWinamp(0, WA_IPC.IPC_ISPLAYING);
                if ((result < 0) || (result > 3)) result = 2;
                return result;
            }
        }

        public bool IsPlaying
        {
            get
            {
                return (PlayStatus == 1);
            }
        }

        public bool IsNotPlayingNorPaused
        {
            get
            {
                int ps = PlayStatus;
                return ((ps != 3) && (ps != 1));
            }
        }

        public void ClearPlaylist()
        {
            SendToWinamp(0, WM_USER_MSGS.WA_CLEAR_PLAYLIST);
        }

        public void Play()
        {
            SendToWinamp(0, WM_COMMAND_MSGS.WINAMP_BUTTON2);

        }
        public void Stop()
        {
            SendToWinamp(0, WM_COMMAND_MSGS.WINAMP_BUTTON4);
        }
        public void FadeOut()
        {
            SendToWinamp(0, WM_COMMAND_MSGS.WINAMP_BUTTON4_SHIFT);
        }
        #endregion
    }
}
